﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
using System.Web.Mvc;
using System.Web.UI;

namespace Nasa8x.Mvc.XmlRouting
{
    public static class RouteCollectionExtensions
    {
        public static void MapRoute(
            this System.Web.Routing.RouteCollection routes,
            MvcRouteConfigurationSection section)
        {
            // Manipulate the Ignore List
            foreach (IgnoreItem ignoreItem in section.Ignore)
            {
                RouteValueDictionary ignoreConstraints = new RouteValueDictionary();

                foreach (Constraint constraint in ignoreItem.Constraints)
                    ignoreConstraints.Add(constraint.Name, constraint.Value);

                IgnoreRoute(routes, ignoreItem.Url, ignoreConstraints);
            }

            // Manipluate the Routing Table
            foreach (RoutingItem routingItem in section.Map)
            {
                RouteValueDictionary defaults = new RouteValueDictionary();
                RouteValueDictionary constraints = new RouteValueDictionary();

                if (routingItem.Controller != string.Empty)
                    defaults.Add("controller", routingItem.Controller);

                if (routingItem.Action != string.Empty)
                    defaults.Add("action", routingItem.Action);

                foreach (Parameter param in routingItem.Paramaters)
                {
                    defaults.Add(param.Name, param.Value);
                    if (!string.IsNullOrEmpty(param.Constraint))
                    {
                        constraints.Add(param.Name, param.Constraint);
                    }
                }

                MapRoute(routes, routingItem.Name, routingItem.Url, defaults, constraints);
            }
        }

        public static void IgnoreRoute
            (RouteCollection routes, string url, RouteValueDictionary constraints)
        {
            if (routes == null)
            {
                throw new ArgumentNullException("routes");
            }
            if (url == null)
            {
                throw new ArgumentNullException("url");
            }
            IgnoreRoute ignore = new IgnoreRoute(url);
            ignore.Constraints = constraints;
            routes.Add(ignore);
        }



        public static void MapRoute(
            RouteCollection routes,
            string name,
            string url,
            RouteValueDictionary defaults,
            RouteValueDictionary constraints)
        {
            if (routes == null)
            {
                throw new ArgumentNullException("routes");
            }
            if (url == null)
            {
                throw new ArgumentNullException("url");
            }
           // var route = new AbsoluteRoute("localhost:8089", url, defaults, constraints, new MvcRouteHandler());


            var route = new Route(url, new MvcRouteHandler());
            route.Defaults = defaults;
            route.Constraints = constraints;

            routes.Add(name, route);
        }



    }


    //sealed class CustomRouteHandler : IRouteHandler
    //{
    //    private string virtualPath;

    //    /// <summary>
    //    /// constructor that takes the virtual path of the requested
    //    /// page
    //    /// </summary>
    //    public CustomRouteHandler(string virtualPath)
    //    {
    //        this.virtualPath = virtualPath;
    //    }

    //    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    //    {
    //        var handler = BuildManager.CreateInstanceFromVirtualPath(this.virtualPath, typeof(ViewPage)) as IHttpHandler;
    //        return handler;

    //    }
    //}



    public class DomainData
    {
        public string Protocol { get; set; }
        public string HostName { get; set; }
        public string Fragment { get; set; }
    }

    sealed class AbsoluteRoute : Route
    {

        private Regex domainRegex;
        private Regex pathRegex;

        public string Domain { get; set; }



        public AbsoluteRoute(string url, IRouteHandler routeHandler)
            : base(url, routeHandler)
        {
        }

        public AbsoluteRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler)
        {
        }

        public AbsoluteRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler)
        {
        }

        public AbsoluteRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            : base(url, defaults, constraints, dataTokens, routeHandler)
        {
        }

        public AbsoluteRoute(string domain, string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
            : base(url, defaults, constraints, routeHandler)
        {
            Domain = domain;
        }

        public AbsoluteRoute(string domain, string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
            : base(url, defaults, routeHandler)
        {
            Domain = domain;
        }

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {


            string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
            var _raw = httpContext.Request.RawUrl;
            //string old = virtualPath;
            if (!string.IsNullOrEmpty(Domain) && _raw.IndexOf(Domain)>=0)
            {

                //virtualPath = string.Format("{0}{1}", Domain, httpContext.Request.RawUrl);

                virtualPath = _raw.Substring(_raw.IndexOf(Domain), _raw.Length - _raw.IndexOf(Domain));

                // virtualPath = string.Concat("~/", (virtualPath.Length <= 8 ? string.Empty : virtualPath.Substring(8)));
                httpContext.RewritePath(virtualPath, true);
            }

            var data = base.GetRouteData(httpContext);



            //var  data=  base.GetRouteData(httpContext);


            // Build regex
            //domainRegex = CreateRegex(Domain);
            //pathRegex = CreateRegex(Url);

            //// Request information
            //string requestDomain = httpContext.Request.Headers["host"];
            //if (!string.IsNullOrEmpty(requestDomain))
            //{
            //    if (requestDomain.IndexOf(":") > 0)
            //    {
            //        requestDomain = requestDomain.Substring(0, requestDomain.IndexOf(":"));
            //    }
            //}
            //else
            //{
            //    requestDomain = httpContext.Request.Url.Host;
            //}
            //string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

            //// Match domain and route
            //Match domainMatch = domainRegex.Match(requestDomain);
            //Match pathMatch = pathRegex.Match(requestPath);

            //// Route data
            //RouteData data = null;
            //if (domainMatch.Success && pathMatch.Success)
            //{
            //    data = new RouteData(this, RouteHandler);

            //    // Add defaults first
            //    if (Defaults != null)
            //    {
            //        foreach (KeyValuePair<string, object> item in Defaults)
            //        {
            //            data.Values[item.Key] = item.Value;
            //        }
            //    }

            //    // Iterate matching domain groups
            //    for (int i = 1; i < domainMatch.Groups.Count; i++)
            //    {
            //        Group group = domainMatch.Groups[i];
            //        if (group.Success)
            //        {
            //            string key = domainRegex.GroupNameFromNumber(i);

            //            if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
            //            {
            //                if (!string.IsNullOrEmpty(group.Value))
            //                {
            //                    data.Values[key] = group.Value;
            //                }
            //            }
            //        }
            //    }

            //    // Iterate matching path groups
            //    for (int i = 1; i < pathMatch.Groups.Count; i++)
            //    {
            //        Group group = pathMatch.Groups[i];
            //        if (group.Success)
            //        {
            //            string key = pathRegex.GroupNameFromNumber(i);

            //            if (!string.IsNullOrEmpty(key) && !char.IsNumber(key, 0))
            //            {
            //                if (!string.IsNullOrEmpty(group.Value))
            //                {
            //                    data.Values[key] = group.Value;
            //                }
            //            }
            //        }
            //    }
            //}

            return data;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            var path = base.GetVirtualPath(requestContext, RemoveDomainTokens(values));

            if (!string.IsNullOrEmpty(Domain))
            {
                path.VirtualPath = string.Format("http://{0}{1}", Domain, requestContext.HttpContext.Request.RawUrl);
            }

            return path;
        }

        public DomainData GetDomainData(RequestContext requestContext, RouteValueDictionary values)
        {
            // Build hostname
            string hostname = Domain;
            foreach (KeyValuePair<string, object> pair in values)
            {
                hostname = hostname.Replace("{" + pair.Key + "}", pair.Value.ToString());
            }

            // Return domain data
            return new DomainData
            {
                Protocol = "http",
                HostName = hostname,
                Fragment = ""
            };
        }

        private Regex CreateRegex(string source)
        {
            // Perform replacements
            source = source.Replace("/", @"\/?");
            source = source.Replace(".", @"\.?");
            source = source.Replace("-", @"\-?");
            source = source.Replace("{", @"(?<");
            source = source.Replace("}", @">([a-zA-Z0-9_]*))");

            return new Regex("^" + source + "$");
        }

        private RouteValueDictionary RemoveDomainTokens(RouteValueDictionary values)
        {
            Regex tokenRegex = new Regex(@"({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?({[a-zA-Z0-9_]*})*-?\.?\/?");
            Match tokenMatch = tokenRegex.Match(Domain);
            for (int i = 0; i < tokenMatch.Groups.Count; i++)
            {
                Group group = tokenMatch.Groups[i];
                if (group.Success)
                {
                    string key = group.Value.Replace("{", "").Replace("}", "");
                    if (values.ContainsKey(key))
                        values.Remove(key);
                }
            }

            return values;
        }

        //public override RouteData GetRouteData(HttpContextBase httpContext) { return null; }

        //public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        //{
        //    using (RouteTable.Routes.GetReadLock())
        //    {
        //        foreach (var routeBase in RouteTable.Routes)
        //        {
        //            if (routeBase != this)
        //            {
        //                var vpd = routeBase.GetVirtualPath(requestContext, values);
        //                if (vpd != null)
        //                    return EnsureCorrectScheme(requestContext, routeBase, vpd);
        //            }
        //        }
        //    }
        //    return null;
        //}

        //private VirtualPathData EnsureCorrectScheme(RequestContext requestContext, RouteBase routeBase, VirtualPathData vpd)
        //{
        //    var route = routeBase as Route;
        //    if (route != null)
        //    {
        //        string desiredScheme = GetDesiredScheme(route);
        //        if (desiredScheme != requestContext.HttpContext.Request.Url.Scheme)
        //            vpd.VirtualPath = MakeAbsoluteUrl(requestContext, vpd.VirtualPath, desiredScheme);
        //    }
        //    return vpd;
        //}

        //private string GetDesiredScheme(Route route)
        //{
        //    if ((route.DataTokens != null) && (route.DataTokens["scheme"] != null))
        //        return (string)route.DataTokens["scheme"];
        //    return "http";
        //}

        //private string MakeAbsoluteUrl(RequestContext requestContext, string virtualPath, string scheme)
        //{
        //    return string.Format("{0}://{1}{2}{3}{4}{5}",
        //        scheme,
        //        requestContext.HttpContext.Request.Url.Host,
        //        GetPortSegmentOfUrl(scheme),
        //        requestContext.HttpContext.Request.ApplicationPath,
        //        requestContext.HttpContext.Request.ApplicationPath.EndsWith("/") ? "" : "/",
        //        virtualPath);
        //}




    }

    sealed class IgnoreRoute : Route
    {
        // Methods
        public IgnoreRoute(string url)
            : base(url, new StopRoutingHandler())
        {
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            return null;
        }
    }
}
